#ifndef		MATH_VECTOR_HEADER
#define		MATH_VECTOR_HEADER

#include	<math.h>
#include	<memory>

#include	<FW/Setup.h>

namespace Math
{
	
	enum Coord
	{
		X, Y, Z, W
	};

	template < class T, U8 Size >
	class Vector
	{
		protected:
			T * mValues;
		public:
			inline Vector ( void )
			{
				mValues = new T [ Size ];
				for ( U8 i = 0; i < Size; i++ )
					this [ i ] = 0;
				//memset ( mValues, 0, sizeof(T) * Size );
			}
			inline Vector ( const T Value, ... )
			{
				mValues = new T [ Size ];
				for ( U8 i = 0; i < Size; i++ )
					this [ i ] = * ( & Value + i );
			}
			inline Vector ( T * Values )
			{
				mValues = new T [ Size ];
				memcpy ( mValues, Values, sizeof(T) * Size );
			}
			inline Vector ( const Vector < T, Size > & Value )
			{
				mValues = new T [ Size ];
				operator = ( Value );
			}
			inline ~Vector ( void )
			{
				delete [ ] mValues;
			}

			inline Vector < T, Size > & operator = ( const Vector < T, Size > & Value )
			{
				for ( U8 i = 0; i < Size; i++ )
					this [ i ] = Value [ i ];
				return * this;
			}
			inline Vector < T, Size > operator - ( void ) const
			{
				Vector < T, Size > Temp;
				for ( U8 i = 0; i < Size; i++ )
					Temp [ i ] = - this [ i ];
				return Temp;
			}

			inline Vector < T, Size > operator + ( const Vector < T, Size > & Value ) const
			{
				Vector < T, Size > Temp;
				for ( U8 i = 0; i < Size; i++ )
					Temp [ i ] = this [ i ] + Value [ i ];
				return Temp;
			}
			inline Vector < T, Size > & operator += ( const Vector < T, Size > & Value )
			{
				for ( U8 i = 0; i < Size; i++ )
					this [ i ] += Value [ i ];
				return * this;
			}
			inline Vector < T, Size > operator + ( const T Value ) const
			{
				Vector < T, Size > Temp;
				for ( U8 i = 0; i < Size; i++ )
					Temp [ i ] = this [ i ] + Value;
				return Temp;
			}
			inline friend Vector < T, Size > operator + ( const T Value1, const Vector < T, Size > & Value2 )
			{
				return Value2 + Value1;
			}
			inline Vector < T, Size > & operator += ( const T Value )
			{
				for ( U8 i = 0; i < Size; i++ )
					this [ i ] += Value;
				return * this;
			}

			inline Vector < T, Size > operator - ( const Vector < T, Size > & Value ) const
			{
				Vector < T, Size > Temp;
				for ( U8 i = 0; i < Size; i++ )
					Temp [ i ] = this [ i ] - Value [ i ];
				return Temp;
			}
			inline Vector < T, Size > & operator -= ( const Vector < T, Size > & Value )
			{
				for ( U8 i = 0; i < Size; i++ )
					this [ i ] -= Value [ i ];
				return * this;
			}
			inline Vector < T, Size > operator - ( const T Value ) const
			{
				Vector < T, Size > Temp;
				for ( U8 i = 0; i < Size; i++ )
					Temp [ i ] = this [ i ] - Value;
				return Temp;
			}
			inline friend Vector < T, Size > operator - ( const T Value1, const Vector < T, Size > & Value2 )
			{
				return - Value2 + Value1;
			}
			inline Vector < T, Size > & operator -= ( const T Value )
			{
				for ( U8 i = 0; i < Size; i++ )
					this [ i ] -= Value;
				return * this;
			}

			inline Vector < T, Size > operator * ( const Vector < T, Size > & Value ) const
			{
				Vector < T, Size > Temp;
				for ( U8 i = 0; i < Size; i++ )
					Temp [ i ] = this [ i ] + Value [ i ];
				return Temp;
			}
			inline Vector < T, Size > & operator *= ( const Vector < T, Size > & Value )
			{
				for ( U8 i = 0; i < Size; i++ )
					this [ i ] *= Value [ i ];
				return * this;
			}
			inline Vector < T, Size > operator * ( const T Value ) const
			{
				Vector < T, Size > Temp;
				for ( U8 i = 0; i < Size; i++ )
					Temp [ i ] = this [ i ] * Value;
				return Temp;
			}
			inline friend Vector < T, Size > operator * ( const T Value1, const Vector < T, Size > & Value2 )
			{
				return Value2 * Value1;
			}
			inline Vector < T, Size > & operator *= ( const T Value )
			{
				for ( U8 i = 0; i < Size; i++ )
					this [ i ] *= Value;
				return * this;
			}

			inline Vector < T, Size > operator / ( const Vector < T, Size > & Value ) const
			{
				Vector < T, Size > Temp;
				for ( U8 i = 0; i < Size; i++ )
					Temp [ i ] = this [ i ] / Value [ i ];
				return Temp;
			}
			inline Vector < T, Size > & operator /= ( const Vector < T, Size > & Value )
			{
				for ( U8 i = 0; i < Size; i++ )
					this [ i ] /= Value [ i ];
				return * this;
			}
			inline Vector < T, Size > operator / ( const T Value ) const
			{
				Vector < T, Size > Temp;
				for ( U8 i = 0; i < Size; i++ )
					Temp [ i ] = this [ i ] / Value;
				return Temp;
			}
			inline friend Vector < T, Size > operator / ( const T Value1, const Vector < T, Size > & Value2 )
			{
				Vector < T, Size > Temp;
				for ( U8 i = 0; i < Size; i++ )
					Temp [ i ] = Value1 / Value2 [ i ];
				return Temp;
			}
			inline Vector < T, Size > & operator /= ( const T Value )
			{
				for ( U8 i = 0; i < Size; i++ )
					this [ i ] /= Value;
				return * this;
			}

			inline bool operator <= ( const Vector < T, Size > & Value ) const
			{
				for ( U8 i = 0; i < Size; i++ )
					if ( ! ( this [ i ] <= Value [ i ] ) )
						return false;
				return true;
			}
			inline bool operator >= ( const Vector < T, Size > & Value ) const
			{
				for ( U8 i = 0; i < Size; i++ )
					if ( ! ( this [ i ] >= Value [ i ] ) )
						return false;
				return true;
			}
			inline bool operator < ( const Vector < T, Size > & Value ) const
			{
				for ( U8 i = 0; i < Size; i++ )
					if ( ! ( this [ i ] < Value [ i ] ) )
						return false;
				return true;
			}
			inline bool operator > ( const Vector < T, Size > & Value ) const
			{
				for ( U8 i = 0; i < Size; i++ )
					if ( ! ( this [ i ] > Value [ i ] ) )
						return false;
				return true;
			}
			inline bool operator == ( const Vector < T, Size > & Value ) const
			{
				for ( U8 i = 0; i < Size; i++ )
					if ( ! ( this [ i ] == Value [ i ] ) )
						return false;
				return true;
			}
			inline bool operator != ( const Vector < T, Size > & Value ) const
			{
				for ( U8 i = 0; i < Size; i++ )
					if ( ! ( this [ i ] != Value [ i ] ) )
						return false;
				return true;
			}

			inline T operator & ( const Vector < T, Size > & Value ) const
			{
				T Sum = ( T ) 0;
				for ( U8 i = 0; i < Size; i++ )
					Sum += this [ i ] * Value [ i ];
				return Sum;
			}

			T & operator [] ( U8 Index )
			{
				return * ( mValues + Index );
			}
			T operator [] ( U8 Index ) const
			{
				return * ( mValues + Index );
			}

			inline T Norm ( void ) const
			{
				return ( * this ) & ( * this );
			}
			inline double Length ( void ) const
			{
				return sqrt ( Norm ( ) );
			}
			inline double Distanse ( const Vector < T, Size > & Value ) const
			{
				return sqrt ( (double)(Value & this) );
			}
			inline void Normalize ( void ) const
			{
				return ( * this ) / Length ( );
			}
			inline double Angle ( const Vector < T, Size > & Value ) const
			{
				return acos ( ( ( * this ) & Value ) / sqrt ( Norm ( ) * Value.Norm ( ) ) );
			}
	};

	template < class T >
	class Vector3 : public Vector < T, 3 >
	{
		public:
			inline Vector < T, 3 > operator ^ ( const Vector < T, 3 > & Value ) const
			{
				return Vector < T, 3 > ( this [ Y ] * Value [ Z ] - this [ Z ] * Value [ Y ], this [ Z ] * Value [ X ]
				        - this [ X ] * Value [ Z ], this [ X ] * Value [ Y ] - this [ Y ] * Value [ X ] );
			}
	};

}

#endif
